/**
 * Copyright (c) 2008, Keisuke Inoue, Syracuse Unviersity
 * All rights reserved.
 *
 */
package edu.syr.iis.web.model;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.SortedSet;

import org.hibernate.Criteria;
import org.hibernate.FetchMode;
import org.hibernate.Hibernate;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Property;
import org.hibernate.criterion.Restrictions;

import edu.syr.iis.web.util.HibernateUtil;

/**
 * @author kinoue
 *
 */
public class AssignmentManager
{
    
    	public List<Assignment> getAllCompletedAssignments(int anno1, int anno2)
    	{
    	    Collection <Assignment> allAssignments = getAssignments(0, Assignment.COMPLETED);
    	    ArrayList <Assignment> assignments = new ArrayList<Assignment>();

    	    for (Assignment assignment: allAssignments)
    	    {
    		Collection<Assignment> coAssignments = assignment.getConversation().getAssignments();
    		if (coAssignments.size() > 1)
		    {
    		    Assignment a = null;
				for (Assignment coAssignment: coAssignments)
			{
			    if (a == null)
				a = coAssignment;
			    else if (coAssignment.getUser().getUserId() == anno1)
				a = coAssignment;
			    else if (a.getUser().getUserId() != anno1 &&
				    coAssignment.getUser().getUserId() == anno2)
				a = coAssignment;    					
			}
			assignments.add(a);
		    } 
		    else 
		    {
			assignments.add(assignment);
		    }
		}
    	    return assignments;    	
    	}
    	
	public List<DisagreementMatrix> getDisagreementMatrices()
	{
		return null;
	}


	
	
	public DisagreementMatrix getDisagreementMatrix(User u1, User u2)
	{
		
		Assignment assignment = getAssignment(277);

		DisagreementMatrix matrix = new DisagreementMatrix();
		Set<DialogueAct> annotationSet = new HashSet<DialogueAct>();
		Set<String> annotationLabelSet = new HashSet<String>();

		matrix.annotator1 = (User) assignment.conversation.getAnnotators().toArray()[0];
		matrix.annotator2 = (User) assignment.conversation.getAnnotators().toArray()[1];	    

		for (Message message: assignment.conversation.getMessages())
		{
			Set<DialogueAct> das = message.getDialogueActs(); 
			annotationSet.addAll(das);
			for (DialogueAct da: das)
			{
				// Cares only Function for now
				annotationLabelSet.add(da.getFunction().getLabel());
			}	    		
		}

		annotationLabelSet.add("NA"); // Always add NA.

		matrix.annotations = new ArrayList<DialogueAct>(annotationSet);	    
		matrix.annotationLabels = new ArrayList<String>(annotationLabelSet);

		matrix.matrix = new int[matrix.annotationLabels.size()][matrix.annotationLabels.size()];

		for (Message message: assignment.conversation.getMessages())
		{
			SortedSet<DialogueAct> daSet1 = message.getSortedDialogueActs(matrix.annotator1);
			SortedSet<DialogueAct> daSet2 = message.getSortedDialogueActs(matrix.annotator2);

			DialogueAct[] das1 = new DialogueAct[daSet1.size()];
			das1 = daSet1.toArray(das1);

			DialogueAct[] das2 = new DialogueAct[daSet2.size()];
			das2 = daSet2.toArray(das1);

			int i = 0;
			for (i = 0; i < das1.length && i < das2.length; i++)
			{
				DialogueActLabel f1 = das1[i].getFunction();
				DialogueActLabel f2 = das2[i].getFunction();

				int index1;
				int index2;

				if (f1 != null)
					index1 = matrix.annotationLabels.indexOf(f1.getLabel());
				else
					index1 = matrix.annotationLabels.indexOf("NA");

				if (f2 != null)
					index2 = matrix.annotationLabels.indexOf(f2.getLabel());
				else
					index2 = matrix.annotationLabels.indexOf("NA");

				matrix.matrix[index1][index2]++;
			}

			if (das1.length < das2.length)
				while (i < das2.length)
				{
					int index1 = matrix.annotationLabels.indexOf("NA");
					int index2 = matrix.annotationLabels.indexOf(das2[i].getFunction().getLabel());
					matrix.matrix[index1][index2]++;
					i++;
				}
			else
				while (i < das1.length)
				{
					int index1 = matrix.annotationLabels.indexOf(das1[i].getFunction().getLabel());
					int index2 = matrix.annotationLabels.indexOf("NA");
					matrix.matrix[index1][index2]++;
					i++;
				}
		}
		return matrix;
	}


	public DisagreementMatrix getDisagreementMatrix(Assignment assignment)
	{
		//Assignment assignment = getAssignment(277);

		DisagreementMatrix matrix = new DisagreementMatrix();
		Set<DialogueAct> annotationSet = new HashSet<DialogueAct>();
		Set<String> annotationLabelSet = new HashSet<String>();

		matrix.annotator1 = (User) assignment.conversation.getAnnotators().toArray()[0];
		matrix.annotator2 = (User) assignment.conversation.getAnnotators().toArray()[1];	    

		for (Message message: assignment.conversation.getMessages())
		{
			Set<DialogueAct> das = message.getDialogueActs(); 
			annotationSet.addAll(das);
			for (DialogueAct da: das)
			{
				// Cares only Function for now
				annotationLabelSet.add(da.getFunction().getLabel());
			}	    		
		}

		annotationLabelSet.add("NA"); // Always add NA.

		matrix.annotations = new ArrayList<DialogueAct>(annotationSet);	    
		matrix.annotationLabels = new ArrayList<String>(annotationLabelSet);

		matrix.matrix = new int[matrix.annotationLabels.size()][matrix.annotationLabels.size()];

		for (Message message: assignment.conversation.getMessages())
		{
			SortedSet<DialogueAct> daSet1 = message.getSortedDialogueActs(matrix.annotator1);
			SortedSet<DialogueAct> daSet2 = message.getSortedDialogueActs(matrix.annotator2);

			DialogueAct[] das1 = new DialogueAct[daSet1.size()];
			das1 = daSet1.toArray(das1);

			DialogueAct[] das2 = new DialogueAct[daSet2.size()];
			das2 = daSet2.toArray(das1);

			int i = 0;
			for (i = 0; i < das1.length && i < das2.length; i++)
			{
				DialogueActLabel f1 = das1[i].getFunction();
				DialogueActLabel f2 = das2[i].getFunction();

				int index1;
				int index2;

				if (f1 != null)
					index1 = matrix.annotationLabels.indexOf(f1.getLabel());
				else
					index1 = matrix.annotationLabels.indexOf("NA");

				if (f2 != null)
					index2 = matrix.annotationLabels.indexOf(f2.getLabel());
				else
					index2 = matrix.annotationLabels.indexOf("NA");

				matrix.matrix[index1][index2]++;
			}

			if (das1.length < das2.length)
				while (i < das2.length)
				{
					int index1 = matrix.annotationLabels.indexOf("NA");
					int index2 = matrix.annotationLabels.indexOf(das2[i].getFunction().getLabel());
					matrix.matrix[index1][index2]++;
					i++;
				}
			else
				while (i < das1.length)
				{
					int index1 = matrix.annotationLabels.indexOf(das1[i].getFunction().getLabel());
					int index2 = matrix.annotationLabels.indexOf("NA");
					matrix.matrix[index1][index2]++;
					i++;
				}
		}
		return matrix;
	}

	/*
	 * getAssigments Variants:
	 * 
	 *  List<Assignment> getAssignments()  -- get all assignments, greedy 
	 *  List<Assignment> getAssignments(boolean lazy) -- get all assignments, greedy or lazy
	 *  
	 *  List<Assignment> getAssignments(User user)
	 *
	 *  List<Assignment> getAssignments(User u1, User u2)
	 *  List<Assignment> getAssignments(int datasetId, User user)
	 *  
	 *   
	 */

	public List<Assignment> getAssignments()
	{
		return getAssignments(false);
	}

	public List<Assignment> getAssignments(boolean lazy)
	{
		List<Assignment> assignments;

		Session session = HibernateUtil.getSessionFactory().getCurrentSession();
		session.beginTransaction();

		try
		{
			assignments = session.createQuery("FROM Assignment").list();
			if (!lazy)
				greedyFetch(assignments);
			session.getTransaction().commit();
		}
		catch (HibernateException e)
		{
			session.getTransaction().rollback();
			throw e;
		}
		return assignments;	
	}

	public List<Assignment> getAssignments(User user)
	{
		return getAssignments(0, user);
	}
	
	public List<Assignment> getAssignments(int status)
	{
		return getAssignments(0, status);
	}
	
	public List<Assignment> getAssignments(int datasetId, int status, int limit)
	{
		List<Assignment> list = null;
		Session session = HibernateUtil.getSessionFactory().getCurrentSession();
		session.beginTransaction();

		try
		{
			String query;
			if (datasetId == 0)  				// for all the dataset
			{
				query= "FROM Assignment WHERE status = :status" +
				" ORDER BY conversationId LIMIT ";
				list = session.createQuery(query).setMaxResults(limit).setInteger("status", status).list();
			} 
			else 				// for a specific dataset 							
			{
				Dataset dataset = (Dataset) session.get(Dataset.class, datasetId);
				query= "FROM Assignment a" +
				" WHERE conversation.dataset = :dataset" +
				" AND status = :status" + 
				" ORDER BY conversation.conversationId";
				list = session.createQuery(query).setMaxResults(limit).setEntity("dataset", dataset).setInteger("status", status).list();
			}
			greedyFetch(list);
			session.getTransaction().commit();
		}
		catch (HibernateException e)
		{
			session.getTransaction().rollback();
			throw e;
		}
		return list;
	}
	
	public List<Assignment> getAssignments(int datasetId, int status)
	{
		List<Assignment> list = null;
		Session session = HibernateUtil.getSessionFactory().getCurrentSession();
		session.beginTransaction();

		try
		{
			String query;
			if (datasetId == 0)  				// for all the dataset
			{
				query= "FROM Assignment WHERE status = :status" +
				" ORDER BY conversationId";
				list = session.createQuery(query).setInteger("status", status).list();
			} 
			else 				// for a specific dataset 							
			{
				Dataset dataset = (Dataset) session.get(Dataset.class, datasetId);
				query= "FROM Assignment a" +
				" WHERE conversation.dataset = :dataset" +
				" AND status = :status" + 
				" ORDER BY conversation.conversationId";
				list = session.createQuery(query).setEntity("dataset", dataset).setInteger("status", status).list();
			}
			greedyFetch(list);
			session.getTransaction().commit();
		}
		catch (HibernateException e)
		{
			session.getTransaction().rollback();
			throw e;
		}
		return list;
	}
	
	
	public List<Assignment> getAssignments(int datasetId, User user)
	{
		List<Assignment> list = null;
		Session session = HibernateUtil.getSessionFactory().getCurrentSession();
		session.beginTransaction();

		try
		{
			String query;
			if (datasetId == 0)  				// for all the dataset
			{
				query= "FROM Assignment WHERE user = :user" +
				" ORDER BY conversationId";
				list = session.createQuery(query).setEntity("user", user).list();
			} 
			else 				// for a specific dataset 							
			{
				Dataset dataset = (Dataset) session.get(Dataset.class, datasetId);
				query= "FROM Assignment a" +
				" WHERE conversation.dataset = :dataset" +
				" AND user = :user" + 
				" ORDER BY conversation.conversationId";
				list = session.createQuery(query).setEntity("dataset", dataset).setEntity("user", user).list();
			}
			greedyFetch(list);
			session.getTransaction().commit();
		}
		catch (HibernateException e)
		{
			session.getTransaction().rollback();
			throw e;
		}
		return list;
	}

	public Assignment getAssignment(Conversation conversation, User user)
	{
		Assignment assignment;
		Session session = HibernateUtil.getSessionFactory().getCurrentSession();
		session.beginTransaction();

		try
		{
			assignment = (Assignment) session
			.createQuery("FROM Assignment WHERE user = :user" +
			"AND conversation = :conversation")
			.setEntity("user", user)
			.setEntity("conversation", conversation)
			.uniqueResult();
			// Always not lazy  
			Hibernate.initialize(assignment.user);
			Hibernate.initialize(assignment.conversation);		
			session.getTransaction().commit();
		}
		catch (HibernateException e)
		{
			session.getTransaction().rollback();
			throw e;
		}
		return assignment;
	}	

	public Assignment getAssignment(int assignmentId)
	{
		Assignment assignment;
		Session session = HibernateUtil.getSessionFactory().getCurrentSession();
		session.beginTransaction();

		try
		{
			assignment = (Assignment) session
			.createQuery("FROM Assignment WHERE assignmentId = :assignmentId")
			.setInteger("assignmentId", assignmentId)
			.uniqueResult();
			// Always not lazy  
			if (assignment != null)
				greedyFetch(assignment);

			session.getTransaction().commit();
		}
		catch (HibernateException e)
		{
			session.getTransaction().rollback();
			throw e;
		}
		return assignment;
	}

	/**
	 * Saves a Assignment object.
	 */
	public void saveAssignment(Assignment Assignment)
	{
		Session session = HibernateUtil.getSessionFactory()
		.getCurrentSession();
		session.beginTransaction();
		try
		{
			session.saveOrUpdate(Assignment);
			session.getTransaction().commit();
		}
		catch (HibernateException e)
		{
			session.getTransaction().rollback();
			throw e;
		}
	}

	/**
	 * Deletes Assignment record with matching AssignmentId 
	 */
	public void deleteAssignment(int AssignmentId)
	{
		Session session = 
			HibernateUtil.getSessionFactory().getCurrentSession();
		session.beginTransaction();	
		try
		{
			Object toDelete = session.get(Assignment.class,new Integer(AssignmentId));

			if (toDelete != null) { 
				session.delete(toDelete);
			}

			session.flush();
			session.getTransaction().commit();
		}
		catch (HibernateException e)
		{
			session.getTransaction().rollback();
			throw e;
		}
	}

	void greedyFetch(List<Assignment> assignments)
	{
		for (Assignment assignment: assignments)
			greedyFetch(assignment);
	}

	void greedyFetch(Assignment assignment)
	{
		if (assignment != null)
		{
			Hibernate.initialize(assignment.user);
			Hibernate.initialize(assignment.conversation);		
			Hibernate.initialize(assignment.conversation.messages);
			for (Message message: assignment.conversation.messages)
				Hibernate.initialize(message.dialogueActs);
			Hibernate.initialize(assignment.conversation.assignments);
		}
	}

}
